package com.jvyou.common.utils;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;

import java.text.MessageFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 查询条件包装器工具类
 *
 * @author 橘柚
 * @since 2024/11/1 15:56
 */
public class WrapperTool {

    /**
     * 判断值是否为空
     *
     * @param value 需要判断的值
     * @return 是否为空
     */
    private static boolean isEmpty(Object value) {
        if (value instanceof String) {
            return StringUtils.isEmpty((String) value);
        }
        return value == null;
    }

    /**
     * 获取当天的查询条件
     *
     * @param queryWrapper 查询条件包装器
     * @param dateField    需要比较的日期字段
     * @param <T>          实体类类型
     */
    public static <T> void todayRecords(LambdaQueryWrapper<T> queryWrapper, SFunction<T, ?> dateField) {
        // 获取当天的开始时间和结束时间
        LocalDate today = LocalDate.now();
        LocalDateTime startOfDay = today.atStartOfDay();
        LocalDateTime endOfDay = today.plusDays(1).atStartOfDay();
        queryWrapper.ge(dateField, startOfDay).lt(dateField, endOfDay);
    }

    /**
     * 获取当天的查询条件
     *
     * @param queryWrapper 查询条件包装器
     * @param dateField    需要比较的日期字段
     * @param <T>          实体类类型
     */
    public static <T> void todayRecords(QueryWrapper<T> queryWrapper, String dateField) {
        // 获取当天的开始时间和结束时间
        LocalDate today = LocalDate.now();
        LocalDateTime startOfDay = today.atStartOfDay();
        LocalDateTime endOfDay = today.plusDays(1).atStartOfDay();
        queryWrapper.ge(dateField, startOfDay).lt(dateField, endOfDay);
    }

    /**
     * 时间范围查询
     *
     * @param queryWrapper 查询条件包装器
     * @param dateField    需要比较的日期字段
     * @param startDate    开始时间
     * @param endDate      结束时间
     * @param <T>          实体类类型
     */
    public static <T> void betweenDate(LambdaQueryWrapper<T> queryWrapper, SFunction<T, ?> dateField, LocalDate startDate, LocalDate endDate) {
        endDate = endDate.plusDays(1);
        queryWrapper.between(dateField, startDate, endDate);
    }

    /**
     * 时间范围查询
     *
     * @param queryWrapper 查询条件包装器
     * @param dateField    需要比较的日期字段
     * @param startDate    开始时间
     * @param endDate      结束时间
     * @param <T>          实体类类型
     */
    public static <T> void betweenDate(QueryWrapper<T> queryWrapper, String dateField, LocalDate startDate, LocalDate endDate) {
        LocalDateTime startTime = startDate.atStartOfDay();
        LocalDateTime endTime = endDate.plusDays(1).atStartOfDay();
        queryWrapper.between(dateField, startTime, endTime);
    }

    /**
     * 时间范围查询（增强版）
     * - 支持空值检查
     * - 支持单边日期查询（如仅开始时间或仅结束时间有效）
     *
     * @param queryWrapper 查询条件包装器
     * @param dateField    需要比较的日期字段
     * @param startDate    开始时间（可为空）
     * @param endDate      结束时间（可为空）
     * @param <T>          实体类类型
     */
    public static <T> void betweenDateTime(QueryWrapper<T> queryWrapper, String dateField, LocalDateTime startDate, LocalDateTime endDate) {
        // 1. 两个日期均不为空：使用 between
        if (startDate != null && endDate != null) {
            queryWrapper.between(dateField, startDate, endDate);
        }
        // 2. 仅开始时间不为空：>= startDate
        else if (startDate != null) {
            queryWrapper.ge(dateField, startDate);
        }
        // 3. 仅结束时间不为空：<= endDate
        else if (endDate != null) {
            queryWrapper.le(dateField, endDate);
        }
    }

    /**
     * 时间范围查询（数组参数版）
     * - 支持通过数组传递时间范围（长度必须为 2）
     * - 自动处理空值、单边查询
     * - 与原有方法逻辑一致，保持代码统一性
     *
     * @param queryWrapper 查询条件包装器
     * @param dateField    需要比较的日期字段
     * @param times        时间范围数组（长度为 2：[startDate, endDate]）
     * @param <T>          实体类类型
     */
    public static <T> void betweenDateTime(QueryWrapper<T> queryWrapper, String dateField, LocalDateTime[] times) {
        if (times == null || times.length == 0) {
            return;
        }
        LocalDateTime startDate = times[0];
        LocalDateTime endDate = times.length >= 2 ? times[1] : null;
        betweenDateTime(queryWrapper, dateField, startDate, endDate);
    }

    /**
     * 时间范围查询（增强版）
     * - 支持空值检查
     * - 支持单边日期查询（如仅开始时间或仅结束时间有效）
     *
     * @param queryWrapper 查询条件包装器
     * @param dateField    需要比较的日期字段
     * @param startDate    开始时间（可为空）
     * @param endDate      结束时间（可为空）
     * @param <T>          实体类类型
     */
    public static <T> void betweenDateTime(LambdaQueryWrapper<T> queryWrapper, SFunction<T, ?> dateField, LocalDateTime startDate, LocalDateTime endDate) {
        if (Objects.nonNull(startDate) && Objects.nonNull(endDate)) {
            queryWrapper.between(dateField, startDate, endDate);
        } else if (startDate != null) {
            queryWrapper.ge(dateField, startDate);
        } else if (endDate != null) {
            queryWrapper.le(dateField, endDate);
        }
    }

    /**
     * 时间范围查询（数组参数版）
     * - 支持通过数组传递时间范围（长度必须为 2）
     * - 自动处理空值、单边查询
     * - 与原有方法逻辑一致，保持代码统一性
     *
     * @param queryWrapper 查询条件包装器
     * @param dateField    需要比较的日期字段
     * @param times        时间范围数组（长度为 2：[startDate, endDate]）
     * @param <T>          实体类类型
     */
    public static <T> void betweenDateTime(LambdaQueryWrapper<T> queryWrapper, SFunction<T, ?> dateField, LocalDateTime[] times) {
        if (times == null || times.length == 0) {
            return;
        }
        LocalDateTime startDate = times[0];
        LocalDateTime endDate = times.length >= 2 ? times[1] : null;
        betweenDateTime(queryWrapper, dateField, startDate, endDate);
    }

    /**
     * 非空查询
     *
     * @param queryWrapper 查询条件包装器
     * @param dateField    非空的数据库查询字段
     * @param <T>          实体类类型
     */
    public static <T> void notEmpty(QueryWrapper<T> queryWrapper, String dateField) {
        queryWrapper.and(wrapper -> wrapper.isNotNull(dateField).ne(dateField, ""));
    }

    /**
     * 非空查询
     *
     * @param queryWrapper 查询条件包装器
     * @param dateField    非空的数据库查询字段
     * @param <T>          实体类类型
     */
    public static <T> void notEmpty(LambdaQueryWrapper<T> queryWrapper, SFunction<T, ?> dateField) {
        queryWrapper.and(wrapper -> wrapper.isNotNull(dateField).ne(dateField, ""));
    }

    /**
     * 不为空做等值查询
     *
     * @param queryWrapper 查询条件包装器
     * @param dateField    非空的数据库查询字段
     * @param value        查询值
     * @param <T>          实体类类型
     */
    public static <T> void notEmptyEq(QueryWrapper<T> queryWrapper, String dateField, Object value) {
        queryWrapper.eq(!isEmpty(value), dateField, value);
    }

    /**
     * 不为空做等值查询
     *
     * @param queryWrapper 查询条件包装器
     * @param dateField    非空的数据库查询字段
     * @param value        查询值
     * @param <T>          实体类类型
     */
    public static <T> void notEmptyEq(LambdaQueryWrapper<T> queryWrapper, SFunction<T, ?> dateField, Object value) {
        queryWrapper.eq(!isEmpty(value), dateField, value);
    }

    /**
     * 不为空做等值查询
     *
     * @param queryWrapper 查询条件包装器
     * @param dateField    非空的数据库查询字段
     * @param value        查询值
     * @param <T>          实体类类型
     */
    public static <T> void notEmptyEq(QueryWrapper<T> queryWrapper, SFunction<T, ?> dateField, Object value) {
        queryWrapper.lambda().eq(!isEmpty(value), dateField, value);
    }

    /**
     * 不为空做模糊查询
     *
     * @param queryWrapper 查询条件包装器
     * @param dateField    非空的数据库查询字段
     * @param value        查询值
     * @param <T>          实体类类型
     */
    public static <T> void notEmptyLike(QueryWrapper<T> queryWrapper, String dateField, Object value) {
        queryWrapper.like(!isEmpty(value), dateField, value);
    }

    /**
     * 不为空做模糊查询
     *
     * @param queryWrapper 查询条件包装器
     * @param dateField    非空的数据库查询字段
     * @param value        查询值
     * @param <T>          实体类类型
     */
    public static <T> void notEmptyLike(LambdaQueryWrapper<T> queryWrapper, SFunction<T, ?> dateField, Object value) {
        queryWrapper.like(!isEmpty(value), dateField, value);
    }

    /**
     * 不为空做模糊查询
     *
     * @param queryWrapper 查询条件包装器
     * @param dateField    非空的数据库查询字段
     * @param value        查询值
     * @param <T>          实体类类型
     */
    public static <T> void notEmptyLike(QueryWrapper<T> queryWrapper, SFunction<T, ?> dateField, Object value) {
        queryWrapper.lambda().like(!isEmpty(value), dateField, value);
    }

    /**
     * 等值查询
     *
     * @param queryWrapper 查询条件包装器
     * @param dateField    非空的数据库查询字段
     * @param value        查询值
     * @param <T>          实体类类型
     */
    public static <T> void eq(QueryWrapper<T> queryWrapper, SFunction<T, ?> dateField, Object value) {
        queryWrapper.lambda().eq(dateField, value);
    }

    /**
     * 等值查询
     *
     * @param queryWrapper 查询条件包装器
     * @param dateField    非空的数据库查询字段
     * @param value        查询值
     * @param <T>          实体类类型
     */
    public static <T> void eq(LambdaQueryWrapper<T> queryWrapper, SFunction<T, ?> dateField, Object value) {
        queryWrapper.eq(dateField, value);
    }

    /**
     * 等值查询
     *
     * @param queryWrapper 查询条件包装器
     * @param dateField    非空的数据库查询字段
     * @param value        查询值
     * @param <T>          实体类类型
     */
    public static <T> void eq(QueryWrapper<T> queryWrapper, String dateField, Object value) {
        queryWrapper.eq(dateField, value);
    }

    /**
     * 判空查询
     *
     * @param queryWrapper 查询条件包装器
     * @param dateField    判空的数据库查询字段
     * @param <T>          实体类类型
     */
    public static <T> void isEmpty(QueryWrapper<T> queryWrapper, String dateField) {
        queryWrapper.and(
                wrapper -> wrapper.isNotNull(dateField)
                        .or()
                        .eq(dateField, "")
        );
    }

    /**
     * 判空查询
     *
     * @param queryWrapper 查询条件包装器
     * @param dateField    判空的数据库查询字段
     * @param <T>          实体类类型
     */
    public static <T> void isEmpty(LambdaQueryWrapper<T> queryWrapper, SFunction<T, ?> dateField) {
        queryWrapper.and(
                wrapper -> wrapper.isNotNull(dateField)
                        .or()
                        .eq(dateField, "")
        );
    }

    /**
     * 科室查询
     *
     * @param queryWrapper 查询条件包装器
     * @param dateField    科室编码字段
     * @param keShiBianMa  科室编码
     * @param <T>          实体类类型
     */
    public static <T> void inKeShi(QueryWrapper<T> queryWrapper, String dateField, String keShiBianMa) {
        if (StringUtils.isEmpty(keShiBianMa)) {
            return;
        }
        queryWrapper.inSql(dateField, MessageFormat.format("(select id from base_organization where (parentId in (''{0}'') or id in (''{0}'') ))", keShiBianMa));
    }

    /**
     * 科室查询
     *
     * @param queryWrapper 查询条件包装器
     * @param dateField    科室编码字段
     * @param keShiBianMa  科室编码
     * @param <T>          实体类类型
     */
    public static <T> void inKeShi(LambdaQueryWrapper<T> queryWrapper, SFunction<T, ?> dateField, String keShiBianMa) {
        if (StringUtils.isEmpty(keShiBianMa)) {
            return;
        }
        queryWrapper.inSql(dateField, MessageFormat.format("(select id from base_organization where (parentId in (''{0}'') or id in (''{0}'') ))", keShiBianMa));
    }

    /**
     * 封装 inSql 方法，动态绑定参数
     *
     * @param lambdaQueryWrapper 查询条件包装器
     * @param dateField          字段引用
     * @param sql                SQL 查询字符串，支持占位符 ?
     * @param args               参数数组，将自动绑定到占位符
     * @param <T>                泛型类型
     */
    public static <T> void inSql(LambdaQueryWrapper<T> lambdaQueryWrapper, SFunction<T, ?> dateField, String sql, Object... args) {
        if (Objects.isNull(lambdaQueryWrapper) || StringUtils.isEmpty(sql) || args == null || args.length == 0) {
            throw new IllegalArgumentException("Invalid queryWrapper or sql statement or args.");
        }
        // 将 ? 替换为具体参数值
        for (Object arg : args) {
            sql = sql.replaceFirst("\\?", "'" + arg + "'");
        }
        lambdaQueryWrapper.inSql(dateField, sql);
    }

    /**
     * 封装 inSql 方法，动态绑定参数
     *
     * @param queryWrapper 查询条件包装器
     * @param dateField    字段引用
     * @param sql          SQL 查询字符串，支持占位符 ?
     * @param args         参数数组，将自动绑定到占位符
     * @param <T>          泛型类型
     */
    public static <T> void inSql(QueryWrapper<T> queryWrapper, String dateField, String sql, Object... args) {
        if (Objects.isNull(queryWrapper) || StringUtils.isEmpty(sql) || args == null || args.length == 0) {
            throw new IllegalArgumentException("Invalid queryWrapper or sql statement or args.");
        }
        // 将 ? 替换为具体参数值
        for (Object arg : args) {
            sql = sql.replaceFirst("\\?", "'" + arg + "'");
        }
        queryWrapper.inSql(dateField, sql);
    }

    /**
     * 执行"in"操作的通用方法，用于在给定字段上进行集合比较
     * 如果提供的数据列表为空或null，会用一个包含空字符串的列表替代，以避免空指针异常
     *
     * @param queryWrapper 实体查询包装器，用于构建查询条件
     * @param dateField    数据库表中的字段名，用于"in"查询
     * @param data         包含查询条件值的列表，用于"in"查询
     * @param <T>          实体类类型
     */
    public static <T> void in(QueryWrapper<T> queryWrapper, String dateField, List<?> data) {
        if (data == null || data.isEmpty()) {
            data = new ArrayList<>();
            data.add(null);
        }
        queryWrapper.in(dateField, data);
    }

    /**
     * 执行"in"操作的通用方法，用于在给定字段上进行集合比较
     * 如果提供的数据列表为空或null，会用一个包含空字符串的列表替代，以避免空指针异常
     *
     * @param queryWrapper 实体查询包装器，用于构建查询条件
     * @param dateField    数据库表中的字段名，用于"in"查询
     * @param data         包含查询条件值的列表，用于"in"查询
     * @param <T>          实体类类型
     */
    public static <T> void in(LambdaQueryWrapper<T> queryWrapper, SFunction<T, ?> dateField, List<?> data) {
        if (data == null || data.isEmpty()) {
            data = new ArrayList<>();
            data.add(null);
        }
        queryWrapper.in(dateField, data);
    }

    /**
     * 执行"in"操作的通用方法，用于在给定字段上进行集合比较
     * 如果提供的数据列表为空或null，会用一个包含空字符串的列表替代，以避免空指针异常
     *
     * @param updateWrapper 实体更新包装器，用于构建更新条件
     * @param dateField     数据库表中的字段名，用于"in"查询
     * @param data          包含查询条件值的列表，用于"in"查询
     * @param <T>           实体类类型
     */
    public static <T> void in(LambdaUpdateWrapper<T> updateWrapper, SFunction<T, ?> dateField, List<?> data) {
        if (data == null || data.isEmpty()) {
            data = new ArrayList<>();
            data.add(null);
        }
        updateWrapper.in(dateField, data);
    }

    /**
     * 执行"in"操作的通用方法，用于在给定字段上进行集合比较
     * 如果提供的数据列表为空或null，会用一个包含空字符串的列表替代，以避免空指针异常
     *
     * @param updateWrapper 实体更新包装器，用于构建更新条件
     * @param dateField     数据库表中的字段名，用于"in"查询
     * @param data          包含查询条件值的列表，用于"in"查询
     * @param <T>           实体类类型
     */
    public static <T> void in(UpdateWrapper<T> updateWrapper, String dateField, List<?> data) {
        if (data == null || data.isEmpty()) {
            data = new ArrayList<>();
            data.add(null);
        }
        updateWrapper.in(dateField, data);
    }
}
